from abc import ABC, abstractmethod
import sys
import re
from urllib.parse import unquote, quote
import requests
from django.utils.http import urlencode
from rest_framework.views import APIView
from rest_framework.response import Response
from django.http import HttpResponseRedirect
from django.views.decorators.csrf import csrf_exempt
from django.utils.decorators import method_decorator
import logging

from user.models import User, ThreeLogin
from tools.myjwt import mjwt
from django.conf import settings

logger = logging.getLogger(__name__)

# 工厂模式
# 抽象工厂类
class OAuthBase(ABC):
    @abstractmethod
    def get_url(self):
        """获取授权URL"""
        pass

    @abstractmethod
    def callback(self, code):
        """处理回调逻辑"""
        pass


# 钉钉具体实现类
class Dingding(OAuthBase):
    def get_url(self):
        client_id = 'dingcxkhsbttemu4b4dc'
        redirect_uri = "http://127.0.0.1:8000/user/dingdingCallback/"
        return f'https://login.dingtalk.com/oauth2/auth?redirect_uri={redirect_uri}&response_type=code&client_id={client_id}&scope=openid&state=dingding&prompt=consent'

    def callback(self, code):
        try:
            # 钉钉应用配置
            client_id = "dingcxkhsbttemu4b4dc"
            client_secret = "OXuv71iocN8gq0O0lILnyJ7WA5WibHumlU2XTpNG3pQJAszD55qNT_LotdJ47GUV"

            # 获取access_token
            token_url = 'https://api.dingtalk.com/v1.0/oauth2/userAccessToken'
            token_data = {
                "clientId": client_id,
                "clientSecret": client_secret,
                "code": code,
                "grantType": "authorization_code"
            }

            headers = {'Content-Type': 'application/json'}
            res = requests.post(token_url, json=token_data, headers=headers, timeout=15)

            if res.status_code != 200:
                raise Exception(f'钉钉token获取失败: {res.text}')

            res_data = res.json()
            access_token = res_data.get('accessToken')

            if not access_token:
                raise Exception('未获取到访问令牌')

            # 获取用户信息
            user_url = 'https://api.dingtalk.com/v1.0/contact/users/me'
            headers = {
                'x-acs-dingtalk-access-token': access_token,
                'Content-Type': 'application/json'
            }

            user_res = requests.get(user_url, headers=headers, timeout=15)

            if user_res.status_code != 200:
                raise Exception('用户信息获取失败')

            user_info = user_res.json()
            dingtalk_id = user_info.get('unionId') or user_info.get('openId')
            username = user_info.get('nick')
            phone = user_info.get('mobile')

            if not dingtalk_id:
                raise Exception('用户信息获取失败')

            # 处理用户登录逻辑
            three_info = ThreeLogin.objects.filter(uid=dingtalk_id, plate='dingding').first()

            if three_info:
                user = three_info.user
                three_info.token = access_token
                three_info.save()
            else:
                if phone:
                    user = User.objects.filter(phone=phone).first()
                    if not user:
                        user = User.objects.create(
                            username=phone,
                            phone=phone,
                            first_name=username if username else '',
                            is_active=True
                        )
                else:
                    username = username or f"ding_{dingtalk_id[-8:]}"
                    user = User.objects.create(
                        username=username,
                        phone='',
                        first_name=username if username else '',
                        is_active=True
                    )

                ThreeLogin.objects.create(
                    uid=dingtalk_id,
                    plate='dingding',
                    token=access_token,
                    user=user
                )

            # 生成JWT token
            payload = {"user_id": user.id}
            jwt_token = mjwt.encode(payload)
            refresh_token = mjwt.encode_refresh_token(payload)

            return {
                'token': jwt_token,
                'refresh_token': refresh_token,
                'user_id': user.id,
                'username': user.username,
                'phone': user.phone or ''
            }

        except Exception as e:
            logger.error(f"钉钉回调处理异常: {e}")
            raise


# Gitee具体实现类
class Gitee(OAuthBase):
    def get_url(self):
        client_id = settings.GITEE_CLIENT_ID
        redirect_uri = "http://localhost:3000/gitee-callback"
        return f'https://gitee.com/oauth/authorize?client_id={client_id}&redirect_uri={redirect_uri}&response_type=code&scope=user_info'

    def callback(self, code):
        try:
            # 通过code获取token
            token_url = 'https://gitee.com/oauth/token'
            token_data = {
                'grant_type': 'authorization_code',
                'code': code,
                'client_id': settings.GITEE_CLIENT_ID,
                'redirect_uri': "http://localhost:3000/gitee-callback",  # 与授权URL保持一致
                'client_secret': settings.GITEE_CLIENT_SECRET
            }

            res = requests.post(token_url, data=token_data)
            res_data = res.json()

            if 'error' in res_data:
                error_msg = res_data.get('error_description', '未知错误')
                raise Exception(f'Gitee授权失败: {error_msg}')

            access_token = res_data.get('access_token')
            if not access_token:
                raise Exception('Gitee授权失败')

            # 获取用户信息
            user_url = 'https://gitee.com/api/v5/user'
            headers = {'Authorization': f'Bearer {access_token}'}
            user_res = requests.get(user_url, headers=headers)
            user_info = user_res.json()

            gitee_id = user_info.get('id')
            username = user_info.get('name')
            email = user_info.get('email') or ''

            if not gitee_id:
                raise Exception('Gitee用户信息获取失败')

            # 处理用户登录逻辑
            three_info = ThreeLogin.objects.filter(uid=gitee_id, plate='gitee').first()

            if three_info:
                user = three_info.user
                three_info.token = access_token
                three_info.save()
            else:
                user = User.objects.create(username=username, email=email)
                ThreeLogin.objects.create(uid=gitee_id, plate='gitee', token=access_token, user=user)

            # 生成JWT token
            payload = {"user_id": user.id}
            jwt_token = mjwt.encode(payload)
            refresh_token = mjwt.encode_refresh_token(payload)

            return {
                'token': jwt_token,
                'refresh_token': refresh_token,
                'user_id': user.id,
                'username': user.username,
                'email': user.email or ''
            }

        except Exception as e:
            logger.error(f"Gitee回调处理异常: {e}")
            raise


# QQ具体实现类
class Qq(OAuthBase):
    def get_url(self):
        qq_auth_url = "https://graph.qq.com/oauth2.0/authorize"
        params = {
            "response_type": "code",
            "client_id": settings.QQ_APP_ID,
            "redirect_uri": settings.QQ_REDIRECT_URI,
            "state": "qq",
            "scope": "get_user_info",
        }
        return f"{qq_auth_url}?{urlencode(params)}"

    def callback(self, code):
        try:
            # 获取access_token
            token_url = "https://graph.qq.com/oauth2.0/token"
            token_params = {
                "grant_type": "authorization_code",
                "client_id": settings.QQ_APP_ID,
                "client_secret": settings.QQ_APP_KEY,
                "code": code,
                "redirect_uri": settings.QQ_REDIRECT_URI,
            }

            token_res = requests.get(token_url, params=token_params, timeout=15)

            if token_res.status_code != 200:
                raise Exception('QQ token获取失败')

            # QQ返回的是text格式，需要解析
            token_text = token_res.text
            if 'access_token=' not in token_text:
                raise Exception('QQ token解析失败')

            access_token = token_text.split('access_token=')[1].split('&')[0]

            # 获取openid
            openid_url = "https://graph.qq.com/oauth2.0/me"
            openid_params = {"access_token": access_token}
            openid_res = requests.get(openid_url, params=openid_params, timeout=15)

            if openid_res.status_code != 200:
                raise Exception('QQ openid获取失败')

            openid_text = openid_res.text
            # 解析openid，格式类似：callback( {"client_id":"xxx","openid":"xxx"} );
            import re
            openid_match = re.search(r'"openid":"([^"]+)"', openid_text)
            if not openid_match:
                raise Exception('QQ openid解析失败')

            openid = openid_match.group(1)

            # 获取用户信息
            user_url = "https://graph.qq.com/user/get_user_info"
            user_params = {
                "access_token": access_token,
                "oauth_consumer_key": settings.QQ_APP_ID,
                "openid": openid,
            }

            user_res = requests.get(user_url, params=user_params, timeout=15)

            if user_res.status_code != 200:
                raise Exception('QQ用户信息获取失败')

            user_info = user_res.json()
            qq_id = openid
            username = user_info.get('nickname', '')

            # 处理用户登录逻辑
            three_info = ThreeLogin.objects.filter(uid=qq_id, plate='qq').first()

            if three_info:
                user = three_info.user
                three_info.token = access_token
                three_info.save()
            else:
                username = username or f"qq_{qq_id[-8:]}"
                user = User.objects.create(
                    username=username,
                    phone='',
                    first_name=username,
                    is_active=True
                )

                ThreeLogin.objects.create(
                    uid=qq_id,
                    plate='qq',
                    token=access_token,
                    user=user
                )

            # 生成JWT token
            payload = {"user_id": user.id}
            jwt_token = mjwt.encode(payload)
            refresh_token = mjwt.encode_refresh_token(payload)

            return {
                'token': jwt_token,
                'refresh_token': refresh_token,
                'user_id': user.id,
                'username': user.username,
                'phone': user.phone or ''
            }

        except Exception as e:
            logger.error(f"QQ回调处理异常: {e}")
            raise


# 微博具体实现类
class Weibo(OAuthBase):
    def get_url(self):
        client_id = "3674019597"
        redirect_uri = "https://localhost:5173/weibo/callback"
        return f"https://api.weibo.com/oauth2/authorize?client_id={client_id}&response_type=code&redirect_uri={redirect_uri}"

    def callback(self, code):
        # 微博的回调逻辑实现
        pass


# 微信具体实现类
class Wechat(OAuthBase):
    def get_url(self):
        app_id = "你的微信开放平台 AppID"
        redirect_uri = quote("http://localhost:3000/wechat-callback")
        params = [
            f"appid={app_id}",
            f"redirect_uri={redirect_uri}",
            "response_type=code",
            "scope=snsapi_login",
            "state=wechat_state"
        ]
        return "https://open.weixin.qq.com/connect/qrconnect?" + "&".join(params) + "#wechat_redirect"

    def callback(self, code):
        # 微信的回调逻辑实现
        pass


# 工厂类
class OAuthFactory:
    @staticmethod
    def create_factory(platform):
        """创建具体的OAuth工厂实例"""
        platform_class = platform.capitalize()
        current_module = sys.modules[__name__]

        try:
            target_class = getattr(current_module, platform_class)
            if issubclass(target_class, OAuthBase):
                return target_class()
        except AttributeError:
            logger.error(f"未找到名为 {platform_class} 的OAuth实现类")

        return None


# 统一的OAuth URL生成接口
class OAuthUrlView(APIView):
    def get(self, request):
        platform = request.query_params.get('type')
        if not platform:
            return Response({'code': 400, 'message': '缺少平台类型参数'})

        factory = OAuthFactory.create_factory(platform)
        if not factory:
            return Response({'code': 400, 'message': '不支持的平台类型'})

        try:
            url = factory.get_url()
            return Response({
                'code': 200,
                'message': f'{platform}登录URL生成成功',
                'data': {'url': url}
            })
        except Exception as e:
            logger.error(f"生成{platform}登录URL失败: {e}")
            return Response({'code': 500, 'message': '生成登录URL失败'})


# 统一的OAuth回调接口
@method_decorator(csrf_exempt, name='dispatch')
class OAuthCallbackView(APIView):
    def get(self, request):
        platform = request.query_params.get('state', '').lower()
        if not platform:
            # 尝试从其他参数推断平台
            if 'dingding' in request.path:
                platform = 'dingding'
            elif 'gitee' in request.path:
                platform = 'gitee'
            elif 'qq' in request.path:
                platform = 'qq'
            elif 'weibo' in request.path:
                platform = 'weibo'
            elif 'wechat' in request.path:
                platform = 'wechat'

        factory = OAuthFactory.create_factory(platform)
        if not factory:
            return Response({'code': 400, 'message': '不支持的平台类型'})

        # 获取授权码
        code = request.query_params.get('code') or request.query_params.get('authCode')
        if not code:
            return Response({'code': 400, 'message': '授权码不存在'})

        try:
            # 处理回调
            result = factory.callback(code)

            # 重定向到前端
            frontend_url = "http://localhost:3000/oauth-callback"
            query_params = [
                f"token={result['token']}",
                f"re_token={result['refresh_token']}",
                f"user_id={result['user_id']}",
                f"username={quote(result['username'])}",
                f"phone={quote(result.get('phone', '') or '')}",
                f"email={quote(result.get('email', '') or '')}",
                f"platform={platform}"
            ]

            redirect_url = f'{frontend_url}?{"&".join(query_params)}'
            return HttpResponseRedirect(redirect_url)

        except Exception as e:
            logger.error(f"{platform}回调处理异常: {e}")
            return Response({
                'code': 500,
                'message': f'{platform}登录处理失败'
            })


# 保留原有的特定平台回调接口（为了兼容性）
class DingTalkCallbackView(APIView):
    def get(self, request):
        # 重定向到统一的回调接口
        response = OAuthCallbackView().get(request)
        return response


class GiteeCallBackView(APIView):
    def get(self, request):
        # 重定向到统一的回调接口
        response = OAuthCallbackView().get(request)
        return response
